package moa.sequencestreams.generator;

import java.util.ArrayList;


import org.apache.commons.math.random.RandomDataImpl;



public class StringSet {
	private   ItemSet items;
	private	  Taxonomy tax;
	private	  int npats;		// number of patterns
	private	  ArrayString[] pat;		// array of patterns
	//ArrayList<ArrayList<Integer>> pat;
	private	  ArrayString answer;
	private	  float[] cum_prob;	// cumulative probabilities of patterns
	
	
	public StringSet
			(int nitems, 		// number of items
			 PatternPar par,	// npats, patlen, corr, conf & conf_var
			 Taxonomy ptax,	// taxonomy (optional)
			 float rept,		// repetition-level
			 float rept_var		// variation in repetition-level*/
			) 
	{
		RandomDataImpl rd;
		rd = new RandomDataImpl();
		/*
		NormalDistributionImpl conf = new NormalDistributionImpl (par.conf, par.conf_var);
		ExponentialDistributionImpl freq = new ExponentialDistributionImpl(nitems);
		ExponentialDistributionImpl corr_lvl = new ExponentialDistributionImpl(rept);
		PoissonDistributionImpl len = new PoissonDistributionImpl (par.getPatlen()-1);	// string length
		NormalDistributionImpl repeat = new NormalDistributionImpl (rept, rept_var);
			UniformDist ud = new UniformDist();*/
			
			items = new ItemSet(nitems, getTax());	// associate probabilities with items
			
			int i, j, num_same;
			float tot;
			
			setNpats(par.getNpats());
			// Creation of a array of string with the number of patterns length
			pat = new ArrayString [getNpats()];
			for (i = 0; i < getNpats(); i++)
			{
				pat[i] = new ArrayString( (int) (1+rd.nextPoisson(par.getPatlen()-1)));
		
				// fill correlated items
				if (par.corr > 0 && i > 0) // correlated patterns
				{	
					
					System.out.println("Fill correlated items");
					// each pattern has some items same as the previous pattern
					num_same = (int) (pat[i].size() * par.corr * rd.nextExponential(rept) + 0.5) ;
					if ( num_same > pat[i-1].size() )
						num_same = pat[i-1].size();
		
					if ( num_same > pat[i].size() )
						num_same = pat[i].size();
					// choose num_same items at random from previous pattern
					Choose shuffle = new Choose (pat[i-1].size(), num_same);
					for (j = 0; j < num_same; j++)
						pat[i].items[j] = pat[i-1].item( shuffle.pos(j) );
				} else 	{	// no correlation
					num_same = 0;
				}
		
				if (rept == 0) 
				{ // LIST OF SEQ 
					System.out.println("Pick item with rept = 0"); 
					// fill remaining items at random
					for (j = num_same; j < pat[i].size(); j++)
					{
						pat[i].items[j] = items.get_item();
						//System.out.print(pat[i].items[j]);
					}	
					//System.out.println(" ");
					//	pat[i]->items[j] = LINT(1 + nitems * rand());
				}	
				else //LIST OF ITEMSETS
				{
					System.out.println("Pick item with rept != 0");
					// some items are repetitions
					float rept_lvl = (float) rd.nextGaussian(rept, rept_var);
					for (j = num_same; j < pat[i].size(); j++)
					{
						//ud = new UniformDist();
						//System.out.println("j " + j );
						if ( j > 0 && rd.nextUniform(0, 1) < rept_lvl )	// pick a previous item
						{
							pat[i].items[j] = pat[i].items[ (int)(j*rd.nextUniform(0, 1)) ];
						}	
						else	// pick random item
						{
							pat[i].items[j] = items.get_item();	
						}			
					}
					//System.out.print(" ");

				}
				pat[i].prob = (float) rd.nextExponential(nitems);	// prob. that this pattern will be picked
				pat[i].conf = (float) rd.nextGaussian(par.conf, par.conf_var);	// used in Transaction::add and CustSeq::add
				// to decide how many items to drop from
				//  this pattern to corrupt it.
		}
		
		if (getTax() != null) 
		{
			// weight probabilites with geometric mean of probabilities of items
			for (i = 0; i < getNpats(); i++)
			{
				double weight = 1;
				for (j = 0; j < pat[i].size(); j++)
					weight *= items.weight(pat[i].items[j]);
			
				//	cerr << "WEIGHT = " << weight;
				weight = Math.pow(weight, (double)(1)/pat[i].size());
				//	cerr << "  " << weight << endl;
				pat[i].prob *= weight;
			}
		}
		
		// normalize probabilites (why -- see get_pat)
		setCum_prob(new float [getNpats()]);
		tot = 0;
		for (i = 0; i < getNpats(); i++)
			tot += pat[i].prob;
		for (i = 0; i < getNpats(); i++)
			pat[i].prob /= tot;
		
		// calulate cumulative probabilities
		getCum_prob()[0] = pat[0].prob;
		for (i = 1; i < getNpats(); i++)
			getCum_prob()[i] = getCum_prob()[i-1] + pat[i].prob;
		//  cerr << cum_prob[npats-1] << endl << flush;
		
		// allocate space for answer
		int maxlen = 0;
		for (i = 1; i < getNpats(); i++)
			if (pat[i].size() > maxlen)
				maxlen = pat[i].size();
		
		answer = new ArrayString(maxlen);
	}
	
	// specialize each item in pattern #i and store result in answer
	//
	public ArrayString specialize(int i)
	{
	  answer.set_size( pat[i].size() );
	  answer.set_conf_lvl( pat[i].conf_lvl() );
	  for (int j = 0; j < pat[i].size(); j++)
	    answer.set_item(j, items.specialize( pat[i].item(j) ));
	  return answer;
	}


	// returns pattern #i
	//
	public ArrayString get_pat(int i)
	{ 
	  if (getTax() == null)
	    return pat[i];
	  else
	    return specialize(i);
	}

	public void setTax(Taxonomy tax) {
		this.tax = tax;
	}

	public Taxonomy getTax() {
		return tax;
	}

	public void setNpats(int npats) {
		this.npats = npats;
	}

	public int getNpats() {
		return npats;
	}

	public void setCum_prob(float[] cum_prob) {
		this.cum_prob = cum_prob;
	}

	public float[] getCum_prob() {
		return cum_prob;
	}
	
	public void display()
	{
	  int i;

	  //items.display();

	  System.out.println("ItemSets:");
	  //setprecision(3);

	  // too lazy to do a sort, so print high-prob. patterns first
	  for (i = 0; i < npats; i++)
	    if (pat[i].prob * npats > 10)
	      pat[i].display(npats);

	  for (i = 0; i < npats; i++)
	    if (pat[i].prob * npats <= 10 && pat[i].prob * npats > 1)
	      pat[i].display(npats);

	 //setprecision(0);

	}



}
